﻿function Node(r) {
	this.record = r;
	this.parent = null;
	this.row = null;
	this.renderer = null;
	this.children = [];
	this.collapsed = false;
	
	this.setChildren = function(nodes) {
		this.children = nodes;
		for (var i=0;i<this.children.length;i++) this.children[i].parent = this;
	};
	this.addChild = function(node) {
		node.parent = this;
		this.children.push(node);
	};
	this.removeChild = function(node) {//TODO
	};
	
	this.getValue = function(field) {
		if (this.renderer == null) {
			node.record[coldef.field]
		} else {
		}
	};
}

function DatagridPlugin() {
	var _this = this;
	this.state = null;
	this.stateChanged = new EventManager();
	this.grid = null;
	this.options = null;
}

/* basic datahandler (no paging, no dynamic scrolling)*/
function DefaultDataHandler() {
	var _this = this;
	this.grid = null;
	this.attach = function(grid) {
		_this.grid = grid;
		_this.grid.getBody().find('.dt-table-records').css('opacity', 0);
		_this.grid.showLoading = false;
		_this.grid.load(0,1, function(data) {
			_this.grid.showLoading = true;
			_this.grid.loading(true);
			
			_this.grid.load(0, data.total, function(data2) {
				_this.grid.getBody().find('.dt-table-records').css('opacity', 1);
				_this.grid.loading(false);
			});
		});
	};
	
	this.detach = function() {
		_this.grid.clear();
	};
}; DefaultDataHandler.inherits(DatagridPlugin);

/* Wrap around the pure data loader to provide loading info + basic tree */
function DefaultViewHandler() {
	var _this = this;
	this.count = -1;
	this.grid = null;
	
	this.get = function(from, to, callback) {
		_this.grid.dataLoader.get(from, to, function(data) {
			_this.count = data.total;
			
			var tree = new Node({root: 'root'});
			for (var i=0;i < data.items.length;i++) tree.addChild(new Node(data.items[i]));
			
			callback(data, tree);
		});
	};
	
	this.attachListener = function() {_this.grid.refresh();};
	
	this.attach = function(grid) {
		this.grid = grid;
		this.grid.loader = this;
		this.grid.attached.add(this.attachListener);
		
		if (this.grid.isAttached) this.grid.refresh();
	};
	
	this.detach = function() {
		_this.grid.attached.remove(this.attachListener);
		_this.grid.loader = null;
	};
}; DefaultViewHandler.inherits(DatagridPlugin);


//simple, group, tree...
function ViewHandler(options) {
	var _this = this;
	this.view = null;
	
	if (options == null) {
		this.options = {mode: 'simple'};
		this.view = new DefaultViewHandler();
	} else if(options.mode =='group') {
		this.view = new GroupViewHandler(options);
	} else if(options.mode =='tree') {
		this.view = new TreeViewHandler(options);
	} else {
		this.view = new DefaultViewHandler();
	}

	this.view.stateChanged.add(function() {
		_this.state = _this.view.state;
	});
	
	this.detach = function() {
		this.view.detach();
	};
	
	this.attach = function(grid) {
		this.view.attach(grid);
	};
}; ViewHandler.inherits(DatagridPlugin);

function PresenterHandler(options) {
	var _this = this;
	this.presenter = null;
	
	if (options == null) {
		this.options = {mode: 'default'};
		this.presenter = new DefaultDataHandler();
	} else if (options.mode == 'paging') {
		this.presenter = new PagingHandler(options);
	} else if (options.mode == 'scroll') {
		this.presenter = new ScrollHandler(options);
	} else {
		this.presenter = new DefaultDataHandler();
	}
	this.presenter.stateChanged.add(function() {
		_this.state = _this.presenter.state;
	});
	
	this.detach = function() {
		this.presenter.detach();
	};
	
	this.attach = function(grid) {
		this.presenter.attach(grid);
	};
}; PresenterHandler.inherits(DatagridPlugin);

function TableTitleHandler(options) {
	this.element = null;
	
	this.setTitle = function(title) {
		this.element.html(title);
	};
	this.showTitle = function(shownornot) {
		this.element.css('display', shownornot ? '': 'none');
	};
	
	this.attach = function(grid) {
		this.element = $('<span>').addClass('dt-table-title').html(options.title);
		grid.getHeader().append(this.element);
	};
	
	this.detach = function() {
		if (this.element != null) this.element.remove();
	};
}; TableTitleHandler.inherits(DatagridPlugin);

function OddEvenRowHandler() {
	var _this = this;
	this.grid = null;
	
	this.doit = function() {
		var rows = _this.grid.getDataRows();
		var k = 0;
		for (var i=0;i < rows.length;i++) {
			var row = $(rows.get(i));
			row.removeClass('dt-table-row-even dt-table-row-odd');
			row.addClass(k % 2 ==0 ? 'dt-table-row-even':'dt-table-row-odd');
			if (row.css('display') != 'none') k++;
		}
	};
	
	this.attach = function(grid) {
		this.grid = grid;
		grid.pageChanged.add(this.doit);
		if (grid.isAttached) {
			this.doit();
		}
	};
	
	this.detach = function() {
		this.grid.pageChanged.remove(this.doit);
		$(this.grid.getDataRows()).removeClass('dt-table-row-even dt-table-row-odd');
	};
}; OddEvenRowHandler.inherits(DatagridPlugin);


function PluginManager(grid) {
	var _this = this;
	this.plugins = new Map();
	this.attach = function() {
		//attach plugins on attach
		Logger.log('Attaching '+_this.plugins.size()+' plugins');
		for (var i=0;i < _this.plugins.size();i++) {
			var ref = _this.plugins.getKeyByIndex(i);
			var plugin = _this.plugins.get(ref);
			Logger.log('Attaching datatable plugin '+ref);
			plugin.attach(grid);
			Logger.log('Plugin ' + ref + ' attached');
			
		}
	};
	
	this.detach = function() {
		Logger.log('Detaching '+_this.plugins.size()+' plugins');
		$(_this.plugins.map).each(function(i, pl) {
			Logger.log('Plugin '+pl.key+'detached');
			pl.value.detach();
		});
	};
	
	grid.attached.add(this.attach);
	
	this.add = function(reference, plugin, options) {
		this.plugins.put(reference, plugin);
		plugin.options = options;
		if (grid.isAttached) {
			plugin.attach(grid);
			Logger.log('Plugin ' + reference + ' attached');
		}
		return grid;
	};
	this.contains = function(reference) {
		return this.plugins.contains(reference);
	};
	this.remove = function(reference) {
		if (this.plugins.contains(reference)) {
			this.plugins.get(reference).detach();
			this.plugins.remove(reference);
		}
	};
	
	this.get = function(reference) {
		return this.plugins.get(reference);
	};
}

function DataTable(dataLoaderOrElement, options) {
	var _this = this;
	this._element = null;
	this.root = null;
	this.options = options;
	this.columnReferences = new Map();
	this.pageChanged = new EventManager();
	this.attached = new EventManager();
	this.resized = new EventManager();
	this.pageContentChanged = new EventManager();
	
	this.showLoading = true;
	this.isAttached = false;
	this.host = null;
	this.loader = null;
	this.dataLoader = dataLoaderOrElement;
	this.plugins = new PluginManager(this);
	
	this.oldcall = null;
	this.load = function(from, to, callback) {
		this.oldcall = {from: from, to: to, callback: callback};
		if (this.showLoading) this.loading(true);
		
		this.loader.get(from, to, function(data, tree) {
			_this.renderPage(tree);
			if (_this.showLoading) _this.loading(false);
			callback(data, tree);
			_this.resize();
		});
	};
	
	this.refresh = function() {
		if (this.oldcall != null) {
			this.load(this.oldcall.from, this.oldcall.to, this.oldcall.callback);
		}
	};
	
	this.getRecord = function(elem) {
		if (elem.tagName == 'TR') return this.getRecordByReference(elem);
		return this.getRecordByReference($(elem).parent('tr').get(0));
	};
	
	this.getNodeForRow = function(r) {
		return this.traverseTree(function(n) {
			if (n.row == r) return n;
		});
	};
	
	this.getAllNodes = function() {
		var nodes = [];
		this.traverseTree(function(n) {
			nodes.push(n);
		});
		return nodes;
	};
	
	this.traverseTree = function(callbackWithNode) {
		var _dive = function(n) {
			var cb = callbackWithNode(n);
			if (cb != null) return cb;
			for (var i=0;i<n.children.length;i++) {
				var cb = _dive(n.children[i]);
				if (cb != null) return cb;
			}
		}
		return _dive(this.root);
	};
	
	this.getSelectedItems = function() {
		var stms = [];
		$(this._element).find('table.dt-table-records tbody tr.row-selected').each(function() {
			stms.push(_this.getRecord(this));
		});
		return stms;
	};
	this.getColumnTh = function(field) {
		var h = this.getHeaderCells();
		for (var i=0;i<h.size();i++) {
			 if (this.getColumnDefinition(h.get(i)).field == field) return h.get(i);
		};
	};
	this.getColumnDefinition = function(th) {
		return this.columnReferences.get(th);
	};
	
	this.getColumnCells = function(th) {
		return $(this._element).find('table.dt-table-records tbody tr td:nth-child('+($(th).index()+1)+')').toArray();
	};
	
	
	this.getCellValue = function(td){
		var coldef = this.getColumnDefinition(this.getHeaderCells().get(td.cellIndex));
		
		var r = this.getRecord(td);
		if (r==null) alert('Could not get cell value');
		return r.record[coldef.field];
	};
	this.setCellValue = function(td, value) {
		$(td).empty();
		$(td).append($('<div>').addClass('dt-table-value').html(value));
		var coldef = this.getColumnDefinition(this.getHeaderCells().get(td.cellIndex));
		this.getRecord(td)[coldef.field] = value;
	};
	
	this.getHeaderCells = function() {return $(this.columnReferences.getKeys());};
	this.getDataRows = function() {return $(this._element).find('tr.dt-table-row');};
	this.getDataCells = function() {return $(this._element).find('tr.dt-table-row > td');};
	this.getCell = function(r, c) {return $(this.getDataRows().get(r)).children('td').get(c);};
	this.getSelectedRows = function() {return $(this._element).find('table.dt-table-records > tbody > tr.selected');};
	this.getBody = function() {return $(this._element).find('.dt-table-body');};
	this.getContent = function() {return $(this._element).find('.dt-table-content');};
	this.getHead = function() {return $(this._element).find('.dt-table-head');};
	this.getHeader = function() {return $(this._element).find('.dt-table-header');};
	this.getFooter = function() {return $(this._element).find('.dt-table-footer');};
	
	this.selectCell = function(cell) {
		if (cell.firstChild.tagName != 'INPUT') {
			var inp = $('<input>').attr('type', 'text').attr('size', 1).val($(cell).html());
			inp.blur(function() {
				$(cell).removeClass('selected');
				$(cell).html(this.value);
			});
			inp.focus(function() {
				$(cell).addClass('selected');
			});
			$(cell).html('');
			$(cell).append(inp);
			inp.focus();
			return false;
		}
	};

	this.clear = function() {
		this._element.find('.dt-table-body table.dt-table-records tbody').empty();
	};
	this.renderPage = function(root, caller) {
		this.root = root;
		var table = this._element.find('.dt-table-body table.dt-table-records tbody');
		
		var h = _this.getHeaderCells(), columns = [];
		for (var j=0;j < h.size();j++) columns.push(_this.getColumnDefinition(h.get(j)));
		
		table.empty();
		
		this.renderTree(table, columns, root, 0);
		this.pageContentChanged.trigger(caller);
		this.pageChanged.trigger(caller);
	};
	
	this.renderTree = function(table, columns, node, level) {
		for (var i=0;i < node.children.length;i++) {
			if (node.children[i] != null) {
				var row = this.renderNode(table, columns, node.children[i], level).addClass('dt-table-level-' + level);

				this.renderTree(table, columns, node.children[i], level+1);
			} else {
				alert('Error child node null');
			}
		}
	};
	
	this.renderNode = function(table, columns, node, level) {
		if (node.renderer == null) node.renderer = this.defaultNodeRenderer;
		
		node.row = node.renderer(table, columns, node, level).get(0)
		table.append(node.row);
		
		return $(node.row);
	};
	
	this.defaultNodeRenderer = function(table, columns, node, level) {
		var row = $('<tr>').addClass('dt-table-row');
		if ( node.parent!=null) row.css('display', node.parent.collapsed ? 'none' : '');
		
		for (var j=0;j < columns.length;j++) {
			var cell = $('<td>').attr('tabindex', 1).addClass('dt-table-cell');
			var value = $('<div>').addClass('dt-table-value');
			cell.append(value);
			row.append(cell);
			if (columns[j].renderer != null)  {
				value.append(columns[j].renderer.render(columns[j], node.record, {level: level,node: node}));
			} else {
				value.html(node.record[columns[j].field]);
			}
		}
		return row;
	};
	
	this.showHeader = function(shownornot) {
		this._element.find('.dt-table-header').css('display', shownornot ? '': 'none');
	};
	this.showFooter = function(shownornot) {
		this._element.find('.dt-table-footer').css('display', shownornot ? '': 'none');
	};
	

	
	this.attach = function(el) {
		Logger.log('Attaching datatable');
		//ensure minimum is loaded.
		if (!this.plugins.contains('toolbar')) this.plugins.add('toolbar', new DatatableToolbarPlugin(this));
		if (!this.plugins.contains('view')) this.plugins.add('view', new ViewHandler(this));
		if (!this.plugins.contains('presenter')) this.plugins.add('presenter', new PresenterHandler(this));
		
		
		if (el != null) {
			$(el).append(this._element);
			this.attached.trigger(el);
			this.host = el;
		} else {
			this.attached.trigger();
		}
		
		this.isAttached = true;
		
		
		$(_this).resize();
	};
	
	this.detach = function() {
		if (dataLoaderOrElement instanceof Element) {
			$(dataLoaderOrElement).show();
		}
		this.plugins.detach();
		this._element.remove();
		this.isAttached = false;
	};
	
	this.resize = function() {
		_this._element.find('.dt-table-head-box').width(_this._element.find('.dt-table-body').get(0).scrollWidth);
		var frs = $(this._element).find('tr.dt-table-row:visible');
		if (frs.size() > 0) {
			var fr = $(frs.get(0)), ths = fr.find('td, div.dt-table-value'), 
			tdivs = _this._element.find('.dt-table-head th,.dt-table-head div.dt-table-value');
		
			tdivs.each(function(index) {$(this).width($(ths.get(index)).width());});
		}
		
		
		//height
		if (_this.options.height != null) {
			var items = ['.dt-table-header', '.dt-table-head', '.dt-table-footer'], dh = 0;
			for (var i=0;i<items.length;i++) {
				var item = _this._element.find(items[i]);
				dh+= item.is(':visible') ? item.height(): 0;
			}
			
			//_this._element.find('.dt-table-body').css('height', _this.options.height - dh);
		} else {
			//_this._element.find('.dt-table-body').css('height', '');
		}
		
		_this.resized.trigger();
	};
	
	this.setHeight = function(h) {
		this.options.height = h;
		this.resize();
	};
	
	this.setWidth = function(w) {
		if (this._element != null) {
			if (w != null) {
				this._element.width(w);
			} else {
				this._element.css('width', '');
			}
			this.resize();
		}
	};
	
	this.loading = function(yesno) {
		var loader = this._element.find('.dt-table-loader'), body = this._element.find('.dt-table-body'), span = this._element.find('.dt-table-loader span');
		if (yesno) {
			loader.show();
			loader.css('left', body.position().left);
			loader.css('top', body.position().top);
			loader.css('width', body.width());
			loader.css('height', body.height());
			loader.height(body.height() );
			span.css('left', (body.width() - span.width()) /2);
			span.css('top', (body.height() - span.height()) /2);
		} else {
			loader.fadeOut();
		}
	};
	
	
	this.initFromDefinition = function() {
		this._element = $("<div>").addClass('datagrid').append(
			$('<div>').addClass('dt-table-header ui-widget-header ui-corner-top'), 
			$('<div>').addClass('dt-table-content').append(
				$('<div>').addClass('dt-table-head-box ui-state-default').css('border', 0).append(
					$('<table><tr class="dt-table-head"></tr></table>')
				),
				$('<div>').addClass('dt-table-body').append(
					$('<div>').addClass('dt-table-loader').append('<span>Loading...</span>'),
					$('<div>').addClass('dt-table-scroll').append(
						$('<table><thead></thead><tbody></tbody></table>').addClass('dt-table-records ui-widget-content').css('border', 0)
					)
				)
			),
			$('<div>').addClass('dt-table-footer ui-widget-header ui-corner-bottom')
		);
		
		
		this._element.find('.dt-table-body').scroll(function(e) {
			_this._element.find('.dt-table-head-box').css('position', 'relative');
			_this._element.find('.dt-table-head-box').css('left', -_this._element.find('.dt-table-body').get(0).scrollLeft);
			return false;
		});
		
		for (var i=0;i < _this.options.columns.length;i++) {
			if (_this.options.columns[i].width && _this.options.height != null) th.css('width', _this.options.columns[i].width);
			
			var th = $('<th>').attr('title', _this.options.columns[i].description).addClass('dt-table-column-header dt-table-cell ui-state-default').
				append($('<div>').addClass('dt-table-value').append(
					$('<span>').html(_this.options.columns[i].description))
				);
				
			this._element.find('.dt-table-head').append(th);
			
			this.columnReferences.put(th.get(0), _this.options.columns[i]);
		}
	};
	
	
	this.initFromMarkup = function() {
		var htmlTable = dataLoaderOrElement;
		this.dataLoader = new MarkupDataLoader(dataLoaderOrElement);
		var cols = $(htmlTable).find('thead th');
		var columns = [];
		for (var i = 0; i < cols.size();i++) {
			columns.push({field: "field_"+i, description: $(cols.get(i)).html()});
		}
		this.options = {columns: columns};
		
		this.initFromDefinition();
		this._element.insertAfter($(htmlTable));
		$(htmlTable).hide();
	};
	
	if (dataLoaderOrElement instanceof Element) {
		this.initFromMarkup();
	} else {
		this.initFromDefinition();
	}
	
	$(window).resize(_this.resize);
	
	return this;
}

function MarkupDataLoader(htmlTable) {
	this.data = [];
	this.get = function(from, to, callback) {
		var _this = this;
		setTimeout(function() {
			callback({
				items: _this.data.slice(from, to),
				total: _this.data.length
			});
		}, $('#delay').val());
	};
	
	this.init = function() {
		var rows = $(htmlTable).find('tbody tr');
		for (var i=0;i < rows.size();i++) {
			var row = rows.get(i), rec = [];
			for (var j=0;j < row.cells.length;j++) {
				rec['field_'+j] = $(row.cells[j]).html();
			}
			
			this.data.push(rec);
		}
	};
	
	this.init();
};

jQuery.fn.extend({datatable: function() {
	var dts = [];
	this.each(function() {
		if (this.tagName == 'TABLE') {
			var dt = new DataTable(this);
			dts.push(dt);
			if ($(this).attr('title') != null) {
				dt.plugins.add('title', new TableTitleHandler({title: $(this).attr('title')}));
			}
			
			dt.attach();
		}
	});
	return $(dts);
}});